﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Validation;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Text;

namespace VsxFactory.Modeling
{
    /// <summary>
    /// Classe permettant de charger la définition d'un modèle
    /// </summary>
    /// <typeparam name="TModelDomain">The type of the model domain.</typeparam>
    /// <typeparam name="TModel">The type of the model root element.</typeparam>
    public abstract class ModelLoader<TModelDomain, TModel> : IDisposable where TModel : ModelElement
    {
        /// <summary>
        /// Represents the method that loads the domain model of type T from a serialized file.
        /// </summary>
        public delegate T ModelLoaderFunction<T>(SerializationResult result, Partition partition, string modelPath, ISchemaResolver resolver, ValidationController controler, ISerializerLocator locator) where T : ModelElement;
        public delegate void ModelSaverFunction<T>(SerializationResult result, T model, string modelPath) where T : ModelElement;

        private readonly IServiceProvider _serviceProvider;
        private readonly ModelLoaderFunction<TModel> _loadCallback;
        private readonly ModelSaverFunction<TModel> _saveCallback;

        /// <summary>
#pragma warning disable 1574
        /// Initializes a new instance of the <see cref="ModelLoader&lt;TModel&gt;"/> class.
#pragma warning restore 1574
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="loadCallback">The load callback.</param>
        /// <param name="saveCallback">The save callback.</param>
        protected ModelLoader(IServiceProvider serviceProvider, ModelLoaderFunction<TModel> loadCallback, ModelSaverFunction<TModel> saveCallback)
        {
            _serviceProvider = serviceProvider;
            _loadCallback = loadCallback;
            _saveCallback = saveCallback;
        }

        /// <summary>
        /// Loads the model.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public TModel LoadModel(string fileName)
        {
            Store store = CreateStore();

            // Ouverture d'une transaction avec l'attribut IsSerializing à True
            using (Transaction transaction = store.TransactionManager.BeginTransaction("Load model", true))
            {
                SerializationResult serializationResult = new SerializationResult();
                TModel model = _loadCallback(serializationResult, store.DefaultPartition, fileName, null, null, null);
                if (serializationResult.Failed)
                {
                    throw new SerializationException(serializationResult);
                }
                transaction.Commit();
                return model;
            }
        }

        /// <summary>
        /// Création d'un store
        /// </summary>
        /// <returns></returns>
        protected Store CreateStore()
        {
            Store store = new Store(_serviceProvider);

            // Initialisation du store avec la définition des modèles          
            store.LoadDomainModels(
                typeof(CoreDesignSurfaceDomainModel), // Obligatoire 
                typeof(TModelDomain)); // Correspond à la classe de votre modèle
            return store;
        }

        /// <summary>
        /// Desctructeur
        /// </summary>
        ~ModelLoader()
        {
            Dispose(false);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected void Dispose(bool disposing)
        {
            if (disposing)
            {
            }
        }

        /// <summary>
        /// Saves the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public void Save(TModel model, string fileName)
        {
            Directory.CreateDirectory(Path.GetDirectoryName(fileName));

            SerializationResult result = new SerializationResult();
            if (_saveCallback != null)
                _saveCallback(result, model, fileName);
            if (result.Failed)
                throw new SerializationException(result);
        }
    }
}